کاوش در تکامل بعدی جاوااسکریپت: Source Phase Imports. راهنمایی جامع برای تحلیل ماژول در زمان ساخت، ماکروها و انتزاعهای بدون هزینه برای توسعهدهندگان جهانی.
ایجاد تحول در ماژولهای جاوااسکریپت: نگاهی عمیق به وارد کردنها در فاز سورس
اکوسیستم جاوااسکریپت در وضعیتی از تکامل دائمی قرار دارد. از آغاز فروتنانهاش به عنوان یک زبان اسکریپتنویسی ساده برای مرورگرها، به یک قدرت جهانی تبدیل شده است که همه چیز را از برنامههای وب پیچیده گرفته تا زیرساختهای سمت سرور به پیش میبرد. سنگ بنای این تکامل، استانداردسازی سیستم ماژول آن، یعنی ماژولهای ES (ESM) بوده است. با این حال، حتی با تبدیل شدن ESM به استاندارد جهانی، چالشهای جدیدی ظهور کردهاند که مرزهای ممکن را جابجا میکنند. این امر به یک پیشنهاد جدید هیجانانگیز و بالقوه تحولآفرین از سوی TC39 منجر شده است: وارد کردنها در فاز سورس (Source Phase Imports).
این پیشنهاد که در حال حاضر در مسیر استانداردسازی در حال پیشرفت است، نشاندهنده یک تغییر بنیادی در نحوه مدیریت وابستگیها توسط جاوااسکریپت است. این پیشنهاد مفهوم «زمان ساخت» یا «فاز سورس» را مستقیماً به زبان وارد میکند و به توسعهدهندگان اجازه میدهد ماژولهایی را وارد کنند که فقط در حین کامپایل اجرا میشوند و بر کد نهایی زمان اجرا تأثیر میگذارند بدون آنکه هرگز بخشی از آن باشند. این امر راه را برای ویژگیهای قدرتمندی مانند ماکروهای بومی، انتزاعهای نوع بدون هزینه، و تولید کد ساده در زمان ساخت، همگی در یک چارچوب استاندارد و امن، باز میکند.
برای توسعهدهندگان در سراسر جهان، درک این پیشنهاد کلید آماده شدن برای موج بعدی نوآوری در ابزارها، فریمورکها و معماری برنامههای جاوااسکریپت است. این راهنمای جامع به بررسی این موضوع میپردازد که وارد کردنها در فاز سورس چه هستند، چه مشکلاتی را حل میکنند، موارد استفاده عملی آنها چیست و تأثیر عمیقی که قرار است بر کل جامعه جهانی جاوااسکریپت بگذارند، چگونه خواهد بود.
تاریخچه مختصری از ماژولهای جاوااسکریپت: مسیر به سوی ESM
برای درک اهمیت وارد کردنها در فاز سورس، ابتدا باید سفر ماژولهای جاوااسکریپت را بفهمیم. برای بخش بزرگی از تاریخ خود، جاوااسکریپت فاقد یک سیستم ماژول بومی بود که منجر به دورهای از راهحلهای خلاقانه اما پراکنده شد.
عصر متغیرهای سراسری و IIFE ها
در ابتدا، توسعهدهندگان وابستگیها را با بارگذاری چندین تگ <script> در یک فایل HTML مدیریت میکردند. این کار فضای نام سراسری (آبجکت window در مرورگرها) را آلوده میکرد و منجر به تداخل متغیرها، ترتیب بارگذاری غیرقابل پیشبینی و یک کابوس نگهداری میشد. یک الگوی رایج برای کاهش این مشکل، عبارت تابعی بلافاصله اجرا شونده (IIFE) بود که یک حوزه خصوصی برای متغیرهای یک اسکریپت ایجاد میکرد و از نشت آنها به حوزه سراسری جلوگیری مینمود.
ظهور استانداردهای جامعه-محور
با پیچیدهتر شدن برنامهها، جامعه راهحلهای قویتری را توسعه داد:
- CommonJS (CJS): این سیستم که توسط Node.js محبوب شد، از یک تابع همگام
require()و یک آبجکتexportsاستفاده میکند. این سیستم برای سرور طراحی شده بود، جایی که خواندن ماژولها از سیستم فایل یک عملیات سریع و مسدودکننده است. ماهیت همگام آن باعث شد که برای مرورگر، جایی که درخواستهای شبکه ناهمگام هستند، کمتر مناسب باشد. - تعریف ماژول ناهمگام (AMD): این سیستم که برای مرورگر طراحی شده بود، (و محبوبترین پیادهسازی آن، RequireJS) ماژولها را به صورت ناهمگام بارگذاری میکرد. سینتکس آن پرجزئیاتتر از CommonJS بود اما مشکل تأخیر شبکه در برنامههای سمت کلاینت را حل میکرد.
استانداردسازی: ماژولهای ES (ESM)
سرانجام، ECMAScript 2015 (ES6) یک سیستم ماژول بومی و استاندارد را معرفی کرد: ماژولهای ES. ESM بهترینهای هر دو دنیا را با یک سینتکس تمیز و اعلانی (import و export) که میتوانست به صورت ایستا تحلیل شود، به ارمغان آورد. این ماهیت ایستا به ابزارهایی مانند باندلرها اجازه میدهد تا بهینهسازیهایی مانند حذف کد مرده (tree-shaking) را قبل از اجرای کد انجام دهند. ESM به گونهای طراحی شده است که ناهمگام باشد و اکنون استاندارد جهانی در مرورگرها و Node.js است و اکوسیستم پراکنده را متحد میکند.
محدودیتهای پنهان ماژولهای ES مدرن
ESM یک موفقیت بزرگ است، اما طراحی آن منحصراً بر رفتار زمان اجرا متمرکز است. یک عبارت import نشاندهنده وابستگیای است که باید هنگام اجرای برنامه دریافت، تجزیه و اجرا شود. این مدل مبتنی بر زمان اجرا، هرچند قدرتمند، چندین چالش ایجاد میکند که اکوسیستم با ابزارهای خارجی و غیراستاندارد در حال حل آنها بوده است.
مشکل ۱: تکثیر وابستگیهای زمان ساخت
توسعه وب مدرن به شدت به یک مرحله ساخت وابسته است. ما از ابزارهایی مانند TypeScript، Babel، Vite، Webpack و PostCSS برای تبدیل کد منبع خود به فرمت بهینهسازی شده برای تولید استفاده میکنیم. این فرآیند شامل وابستگیهای زیادی است که فقط در زمان ساخت مورد نیاز هستند، نه در زمان اجرا.
تایپاسکریپت را در نظر بگیرید. وقتی مینویسید import { type User } from './types'، شما موجودیتی را وارد میکنید که هیچ معادل زمان اجرایی ندارد. کامپایلر تایپاسکریپت این وارد کردن و اطلاعات نوع را در حین کامپایل حذف میکند. با این حال، از دیدگاه سیستم ماژول جاوااسکریپت، این فقط یک وارد کردن دیگر است. باندلرها و موتورها باید منطق خاصی برای مدیریت و دور انداختن این وارد کردنهای «فقط-نوع» داشته باشند، راهحلی که خارج از مشخصات زبان جاوااسکریپت وجود دارد.
مشکل ۲: تلاش برای انتزاعهای بدون هزینه
یک انتزاع بدون هزینه (zero-cost abstraction) ویژگیای است که راحتی سطح بالایی را در طول توسعه فراهم میکند اما به کد بسیار کارآمد و بدون هیچ سربار زمان اجرایی کامپایل میشود. یک مثال عالی، یک کتابخانه اعتبارسنجی است. ممکن است بنویسید:
validate(userSchema, userData);
در زمان اجرا، این شامل یک فراخوانی تابع و اجرای منطق اعتبارسنجی است. چه میشد اگر زبان میتوانست در زمان ساخت، اسکما را تحلیل کرده و کد اعتبارسنجی بسیار خاص و درونخطی تولید کند و فراخوانی تابع عمومی `validate` و آبجکت اسکما را از بسته نهایی حذف کند؟ این کار در حال حاضر به روشی استاندارد غیرممکن است. کل تابع `validate` و آبجکت `userSchema` باید به کلاینت ارسال شوند، حتی اگر اعتبارسنجی میتوانست به شکل دیگری انجام یا پیش-کامپایل شود.
مشکل ۳: عدم وجود ماکروهای استاندارد
ماکروها یک ویژگی قدرتمند در زبانهایی مانند Rust، Lisp و Swift هستند. آنها اساساً کدی هستند که در زمان کامپایل کد مینویسند. در جاوااسکریپت، ما ماکروها را با استفاده از ابزارهایی مانند پلاگینهای Babel یا تبدیلهای SWC شبیهسازی میکنیم. فراگیرترین مثال JSX است:
const element = <h1>Hello, World</h1>;
این جاوااسکریپت معتبر نیست. یک ابزار ساخت آن را به این صورت تبدیل میکند:
const element = React.createElement('h1', null, 'Hello, World');
این تبدیل قدرتمند است اما کاملاً به ابزارهای خارجی متکی است. هیچ راه بومی و درون-زبانی برای تعریف تابعی که این نوع تبدیل سینتکس را انجام دهد وجود ندارد. این عدم استانداردسازی منجر به یک زنجیره ابزار پیچیده و اغلب شکننده میشود.
معرفی وارد کردنها در فاز سورس: یک تغییر پارادایم
وارد کردنها در فاز سورس پاسخی مستقیم به این محدودیتها هستند. این پیشنهاد یک سینتکس جدید اعلان وارد کردن را معرفی میکند که به صراحت وابستگیهای زمان ساخت را از وابستگیهای زمان اجرا جدا میکند.
سینتکس جدید ساده و شهودی است: import source.
import { MyType } from './types.js'; // یک وارد کردن استاندارد و زمان اجرا
import source { MyMacro } from './macros.js'; // یک وارد کردن جدید در فاز سورس
مفهوم اصلی: جداسازی فاز
ایده کلیدی، رسمی کردن دو فاز متمایز از ارزیابی کد است:
- فاز سورس (زمان ساخت): این فاز ابتدا رخ میدهد و توسط یک «میزبان» جاوااسکریپت (مانند یک باندلر، یک رانتایم مانند Node.js یا Deno، یا محیط توسعه/ساخت یک مرورگر) مدیریت میشود. در طول این فاز، میزبان به دنبال اعلانهای
import sourceمیگردد. سپس این ماژولها را در یک محیط ویژه و ایزوله بارگذاری و اجرا میکند. این ماژولها میتوانند کد منبع ماژولهایی را که آنها را وارد کردهاند، بررسی و تبدیل کنند. - فاز زمان اجرا (زمان اجرا): این فازی است که همه ما با آن آشنا هستیم. موتور جاوااسکریپت کد نهایی و بالقوه تبدیل شده را اجرا میکند. تمام ماژولهای وارد شده از طریق
import sourceو کدی که از آنها استفاده کرده است کاملاً از بین رفتهاند؛ آنها هیچ اثری در گراف ماژول زمان اجرا باقی نمیگذارند.
به آن به عنوان یک پیشپردازنده استاندارد، امن و آگاه از ماژول فکر کنید که مستقیماً در مشخصات زبان تعبیه شده است. این فقط جایگزینی متن مانند پیشپردازنده C نیست؛ این یک سیستم عمیقاً یکپارچه است که میتواند با ساختار جاوااسکریپت، مانند درختهای نحو انتزاعی (ASTs) کار کند.
موارد استفاده کلیدی و مثالهای عملی
قدرت واقعی وارد کردنها در فاز سورس زمانی مشخص میشود که به مشکلاتی که میتوانند به زیبایی حل کنند نگاه کنیم. بیایید برخی از تأثیرگذارترین موارد استفاده را بررسی کنیم.
مورد استفاده ۱: حاشیهنویسیهای نوع بومی و بدون هزینه
یکی از محرکهای اصلی این پیشنهاد، فراهم کردن یک خانه بومی برای سیستمهای نوع مانند TypeScript و Flow در خود زبان جاوااسکریپت است. در حال حاضر، `import type { ... }` یک ویژگی خاص تایپاسکریپت است. با وارد کردنها در فاز سورس، این به یک ساختار زبانی استاندارد تبدیل میشود.
فعلی (TypeScript):
// types.ts
export interface User {
id: number;
name: string;
}
// app.ts
import type { User } from './types';
const user: User = { id: 1, name: 'Alice' };
آینده (جاوااسکریپت استاندارد):
// types.js
export interface User { /* ... */ } // با فرض اینکه یک پیشنهاد سینتکس نوع نیز پذیرفته شود
// app.js
import source { User } from './types.js';
const user: User = { id: 1, name: 'Alice' };
مزیت: عبارت import source به وضوح به هر ابزار یا موتور جاوااسکریپتی میگوید که ./types.js یک وابستگی فقط-زمان-ساخت است. موتور زمان اجرا هرگز تلاش نخواهد کرد آن را دریافت یا تجزیه کند. این کار مفهوم حذف نوع را استاندارد میکند، آن را به بخشی رسمی از زبان تبدیل میکند و کار باندلرها، لینترها و سایر ابزارها را ساده میسازد.
مورد استفاده ۲: ماکروهای قدرتمند و بهداشتی (Hygienic)
ماکروها تحولآفرینترین کاربرد وارد کردنها در فاز سورس هستند. آنها به توسعهدهندگان اجازه میدهند تا سینتکس جاوااسکریپت را گسترش دهند و زبانهای خاص دامنه (DSLs) قدرتمندی را به روشی امن و استاندارد ایجاد کنند.
بیایید یک ماکروی لاگبرداری ساده را تصور کنیم که به طور خودکار نام فایل و شماره خط را در زمان ساخت اضافه میکند.
تعریف ماکرو:
// macros.js
export function log(macroContext) {
// 'macroContext' API هایی برای بررسی محل فراخوانی فراهم میکند
const callSite = macroContext.getCallSiteInfo(); // e.g., { file: 'app.js', line: 5 }
const messageArgument = macroContext.getArgument(0); // دریافت AST برای پیام
// بازگرداندن یک AST جدید برای یک فراخوانی console.log
return `console.log("[${callSite.file}:${callSite.line}]", ${messageArgument})`;
}
استفاده از ماکرو:
// app.js
import source { log } from './macros.js';
const value = 42;
log(`The value is: ${value}`);
کد کامپایل شده زمان اجرا:
// app.js (بعد از فاز سورس)
const value = 42;
console.log("[app.js:5]", `The value is: ${value}`);
مزیت: ما یک تابع `log` گویاتر ایجاد کردهایم که اطلاعات زمان ساخت را مستقیماً به کد زمان اجرا تزریق میکند. هیچ فراخوانی تابع `log` در زمان اجرا وجود ندارد، فقط یک `console.log` مستقیم. این یک انتزاع بدون هزینه واقعی است. همین اصل میتواند برای پیادهسازی JSX، styled-components، کتابخانههای بینالمللیسازی (i18n) و موارد دیگر استفاده شود، همگی بدون پلاگینهای سفارشی Babel.
مورد استفاده ۳: تولید کد یکپارچه در زمان ساخت
بسیاری از برنامهها به تولید کد از منابع دیگر، مانند یک اسکیمای GraphQL، یک تعریف Protocol Buffers، یا حتی یک فایل داده ساده مانند YAML یا JSON متکی هستند.
تصور کنید یک اسکیمای GraphQL دارید و میخواهید یک کلاینت بهینهسازی شده برای آن تولید کنید. امروزه، این کار به ابزارهای CLI خارجی و یک تنظیمات ساخت پیچیده نیاز دارد. با وارد کردنها در فاز سورس، این میتواند به بخشی یکپارچه از گراف ماژول شما تبدیل شود.
ماژول تولیدکننده:
// graphql-codegen.js
export function createClient(schemaText) {
// ۱. تجزیه schemaText
// ۲. تولید کد جاوااسکریپت برای یک کلاینت نوعدار
// ۳. بازگرداندن کد تولید شده به عنوان یک رشته
const generatedCode = `
export const client = {
query: { /* ... متدهای تولید شده ... */ }
};
`;
return generatedCode;
}
استفاده از تولیدکننده:
// app.js
// ۱. وارد کردن اسکما به عنوان متن با استفاده از Import Assertions (یک ویژگی جداگانه)
import schema from './api.graphql' with { type: 'text' };
// ۲. وارد کردن تولیدکننده کد با استفاده از وارد کردن در فاز سورس
import source { createClient } from './graphql-codegen.js';
// ۳. اجرای تولیدکننده در زمان ساخت و تزریق خروجی آن
export const { client } = createClient(schema);
مزیت: کل فرآیند اعلانی و بخشی از کد منبع است. اجرای تولیدکننده کد خارجی دیگر یک مرحله جداگانه و دستی نیست. اگر `api.graphql` تغییر کند، ابزار ساخت به طور خودکار میداند که باید فاز سورس را برای `app.js` دوباره اجرا کند. این امر گردش کار توسعه را سادهتر، قویتر و کمتر مستعد خطا میکند.
چگونه کار میکند: میزبان، جعبه شنی (Sandbox) و فازها
مهم است که بدانیم خود موتور جاوااسکریپت (مانند V8 در کروم و Node.js) فاز سورس را اجرا نمیکند. این مسئولیت بر عهده محیط میزبان است.
نقش میزبان
میزبان برنامهای است که کد جاوااسکریپت را کامپایل یا اجرا میکند. این میتواند:
- یک باندلر مانند Vite، Webpack یا Parcel باشد.
- یک رانتایم مانند Node.js یا Deno باشد.
- حتی یک مرورگر میتواند به عنوان میزبان برای کدی که در DevTools آن یا در طول فرآیند ساخت سرور توسعه اجرا میشود، عمل کند.
میزبان فرآیند دو فازی را هماهنگ میکند:
- کد را تجزیه کرده و تمام اعلانهای
import sourceرا کشف میکند. - یک محیط ایزوله و جعبهشنی (که اغلب "Realm" نامیده میشود) به طور خاص برای اجرای ماژولهای فاز سورس ایجاد میکند.
- کد را از ماژولهای سورس وارد شده در این جعبه شنی اجرا میکند. به این ماژولها APIهای ویژهای برای تعامل با کدی که در حال تبدیل آن هستند داده میشود (مثلاً APIهای دستکاری AST).
- تبدیلها اعمال میشوند و در نتیجه کد نهایی زمان اجرا به دست میآید.
- این کد نهایی سپس برای فاز زمان اجرا به موتور جاوااسکریپت معمولی ارسال میشود.
امنیت و جعبهشنی (Sandboxing) حیاتی هستند
اجرای کد در زمان ساخت خطرات امنیتی بالقوهای را به همراه دارد. یک اسکریپت زمان ساخت مخرب میتواند سعی کند به سیستم فایل یا شبکه روی ماشین توسعهدهنده دسترسی پیدا کند. پیشنهاد وارد کردنها در فاز سورس تأکید زیادی بر امنیت دارد.
کد فاز سورس در یک جعبهشنی بسیار محدود اجرا میشود. به طور پیشفرض، به موارد زیر دسترسی ندارد:
- سیستم فایل محلی.
- درخواستهای شبکه.
- متغیرهای سراسری زمان اجرا مانند
windowیاprocess.
هر قابلیتی مانند دسترسی به فایل باید به صراحت توسط محیط میزبان اعطا شود و به کاربر کنترل کامل بر آنچه اسکریپتهای زمان ساخت مجاز به انجام آن هستند، میدهد. این امر آن را بسیار امنتر از اکوسیستم فعلی پلاگینها و اسکریپتها میکند که اغلب دسترسی کاملی به سیستم دارند.
تأثیر جهانی بر اکوسیستم جاوااسکریپت
معرفی وارد کردنها در فاز سورس، موجهایی را در سراسر اکوسیستم جهانی جاوااسکریپت ایجاد خواهد کرد و اساساً نحوه ساخت ابزارها، فریمورکها و برنامهها را تغییر خواهد داد.
برای نویسندگان فریمورک و کتابخانه
فریمورکهایی مانند React، Svelte، Vue و Solid میتوانند از وارد کردنها در فاز سورس برای تبدیل کامپایلرهای خود به بخشی از خود زبان استفاده کنند. کامپایلر Svelte، که کامپوننتهای Svelte را به جاوااسکریپت وانیلی بهینهسازی شده تبدیل میکند، میتواند به عنوان یک ماکرو پیادهسازی شود. JSX میتواند به یک ماکروی استاندارد تبدیل شود و نیاز به اینکه هر ابزار پیادهسازی سفارشی خود را از این تبدیل داشته باشد، از بین ببرد.
کتابخانههای CSS-in-JS میتوانند تمام تجزیه استایل و تولید قوانین ایستا را در زمان ساخت انجام دهند و یک رانتایم حداقلی یا حتی صفر را ارسال کنند که منجر به بهبود قابل توجهی در کارایی میشود.
برای توسعهدهندگان ابزار
برای سازندگان Vite، Webpack، esbuild و دیگران، این پیشنهاد یک نقطه گسترش قدرتمند و استاندارد را ارائه میدهد. به جای تکیه بر یک API پلاگین پیچیده که بین ابزارها متفاوت است، آنها میتوانند مستقیماً به فاز زمان ساخت خود زبان متصل شوند. این میتواند منجر به یک اکوسیستم ابزار متحدتر و قابل تعاملتر شود، جایی که یک ماکروی نوشته شده برای یک ابزار به طور یکپارچه در دیگری کار میکند.
برای توسعهدهندگان برنامه
برای میلیونها توسعهدهندهای که هر روز برنامههای جاوااسکریپت مینویسند، مزایای آن متعدد است:
- پیکربندیهای ساخت سادهتر: وابستگی کمتر به زنجیرههای پیچیده پلاگینها برای کارهای رایج مانند مدیریت TypeScript، JSX یا تولید کد.
- کارایی بهبود یافته: انتزاعهای بدون هزینه واقعی منجر به حجم بستههای کوچکتر و اجرای سریعتر در زمان اجرا خواهد شد.
- تجربه توسعهدهنده پیشرفته: توانایی ایجاد افزونههای سفارشی و خاص دامنه به زبان، سطوح جدیدی از بیانگری را باز کرده و کدهای تکراری را کاهش میدهد.
وضعیت فعلی و مسیر پیش رو
وارد کردنها در فاز سورس یک پیشنهاد است که توسط TC39، کمیتهای که جاوااسکریپت را استاندارد میکند، در حال توسعه است. فرآیند TC39 چهار مرحله اصلی دارد، از مرحله ۱ (پیشنهاد) تا مرحله ۴ (تمام شده و آماده برای گنجاندن در زبان).
از اواخر سال ۲۰۲۳، پیشنهاد «وارد کردنها در فاز سورس» (همراه با همتای آن، ماکروها) در مرحله ۲ قرار دارد. این بدان معناست که کمیته پیشنویس را پذیرفته و فعالانه روی مشخصات دقیق کار میکند. سینتکس و معناشناسی اصلی تا حد زیادی مشخص شدهاند و این مرحلهای است که پیادهسازیها و آزمایشهای اولیه برای ارائه بازخورد تشویق میشوند.
این بدان معناست که شما امروز نمیتوانید از import source در پروژه مرورگر یا Node.js خود استفاده کنید. با این حال، میتوانیم انتظار داشته باشیم که با پیشرفت پیشنهاد به سمت مرحله ۳، پشتیبانی آزمایشی در ابزارهای ساخت و ترنسپایلرهای پیشرفته در آینده نزدیک ظاهر شود. بهترین راه برای مطلع ماندن، دنبال کردن پیشنهادهای رسمی TC39 در GitHub است.
نتیجهگیری: آینده در زمان ساخت است
وارد کردنها در فاز سورس یکی از مهمترین تغییرات معماری در تاریخ جاوااسکریپت از زمان معرفی ماژولهای ES را نشان میدهند. با ایجاد یک جدایی رسمی و استاندارد بین زمان ساخت و زمان اجرا، این پیشنهاد یک شکاف اساسی در زبان را برطرف میکند. این قابلیتهایی را که توسعهدهندگان مدتها آرزوی آن را داشتند - ماکروها، فرابرنامهنویسی زمان کامپایل و انتزاعهای بدون هزینه واقعی - از حوزه ابزارهای سفارشی و پراکنده خارج کرده و به هسته خود جاوااسکریپت میآورد.
این چیزی فراتر از یک قطعه سینتکس جدید است؛ این یک روش جدید برای فکر کردن در مورد نحوه ساخت نرمافزار با جاوااسکریپت است. این به توسعهدهندگان این قدرت را میدهد که منطق بیشتری را از دستگاه کاربر به ماشین توسعهدهنده منتقل کنند، که منجر به برنامههایی میشود که نه تنها قدرتمندتر و گویاتر هستند، بلکه سریعتر و کارآمدتر نیز میباشند. همانطور که این پیشنهاد سفر خود را به سمت استانداردسازی ادامه میدهد، کل جامعه جهانی جاوااسکریپت باید با انتظار تماشا کند. عصر جدیدی از نوآوری در زمان ساخت در افق است.